package com.demo2do.fms.admin.service.supplier.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;

import com.demo2do.core.entity.exception.BusinessException;
import com.demo2do.core.entity.page.Page;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dao;
import com.demo2do.core.utils.CollectionUtils;
import com.demo2do.fms.admin.Constants;
import com.demo2do.fms.admin.dao.supplier.DriverDao;
import com.demo2do.fms.admin.dao.supplier.OrganizationDao;
import com.demo2do.fms.admin.dao.supplier.SupplierDao;
import com.demo2do.fms.admin.dao.supplier.SupplierRelationDao;
import com.demo2do.fms.admin.dao.vehicle.RemoteTransMeansDao;
import com.demo2do.fms.admin.dao.vehicle.TractorDao;
import com.demo2do.fms.admin.dao.vehicle.VehicleDao;
import com.demo2do.fms.admin.dao.vehicle.VehicleSourceDao;
import com.demo2do.fms.admin.entity.export.supplier.VehicleSourceSheet;
import com.demo2do.fms.admin.entity.report.supplier.VehicleSourceReportSearch;
import com.demo2do.fms.admin.entity.search.supplier.VehicleSourceSearch;
import com.demo2do.fms.admin.entity.security.Login;
import com.demo2do.fms.admin.handler.VamHandler;
import com.demo2do.fms.admin.handler.support.FmsJdbcSupport;
import com.demo2do.fms.admin.service.DataServiceImpl;
import com.demo2do.fms.admin.service.supplier.VehicleSourceService;
import com.demo2do.fms.entity.authority.Organization;
import com.demo2do.fms.entity.driver.Driver;
import com.demo2do.fms.entity.driver.DriverAuditStatus;
import com.demo2do.fms.entity.supplier.common.Supplier;
import com.demo2do.fms.entity.vehicle.source.LockStatus;
import com.demo2do.fms.entity.vehicle.source.LockType;
import com.demo2do.fms.entity.vehicle.source.RemoteTransMeans;
import com.demo2do.fms.entity.vehicle.source.SyncStatus;
import com.demo2do.fms.entity.vehicle.source.Vehicle;
import com.demo2do.fms.entity.vehicle.source.VehicleGpsInfo;
import com.demo2do.fms.entity.vehicle.source.VehicleSource;
import com.demo2do.fms.entity.vehicle.source.VehicleSourceEvent;
import com.demo2do.fms.entity.vehicle.source.VehicleSourceEventType;
import com.demo2do.fms.entity.vehicle.source.VehicleSourceLockRecord;
import com.demo2do.fms.entity.vehicle.tractor.Tractor;
import com.demo2do.fms.entity.vehicle.tractor.TractorEvent;
import com.demo2do.fms.entity.vehicle.tractor.TractorEventType;
import com.demo2do.fms.entity.vehicle.trailer.Trailer;
import com.demo2do.fms.entity.vehicle.trailer.TrailerEvent;
import com.demo2do.fms.entity.vehicle.trailer.TrailerEventType;

/**
 *
 * @author Ark
 *
 */
@Service("vehicleSourceService")
public class VehicleSourceServiceImpl extends DataServiceImpl<VehicleSource> implements VehicleSourceService {
    
    private static final Logger logger = LoggerFactory.getLogger(VehicleSourceServiceImpl.class);
    
    @Dao
    private VehicleSourceDao vehicleSourceDao;
    
    @Dao
    private DriverDao driverDao;
    
    @Dao
    private OrganizationDao organizationDao;
    
    @Dao
    private SupplierDao supplierDao;
    
    @Dao
    private SupplierRelationDao supplierRelationDao;
    
    @Dao
    private VehicleDao vehicleDao;
    
    @Dao
    private TractorDao tractorDao;
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Autowired
    private FmsJdbcSupport fmsJdbcSupport;
    
    @Dao
    private RemoteTransMeansDao remoteTransMeansDao;
    
    @Autowired
    private VamHandler vamHandler;
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#count(com.demo2do.fms.admin.entity.security.Login, com.demo2do.fms.entity.vehicle.SyncStatus)
     */
    public int count(Login login, SyncStatus status) {
        
        if (!login.isSupplier()) {
            
            return vehicleSourceDao.count(status);
            
        } else {
            
            Supplier supplier = genericDaoSupport.load(Supplier.class, login.getSupplierId());
            return vehicleSourceDao.count(status, supplier);
            
        }
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#list(com.demo2do.fms.admin.entity.security.Login, com.demo2do.fms.entity.vehicle.SyncStatus, com.demo2do.core.entity.page.Page)
     */
    public List<VehicleSource> list(Login login, SyncStatus status, Page page) {
        
        page.calculate();
        
        if (!login.isSupplier()) {
            
            return vehicleSourceDao.list(status, page.getBeginIndex(), page.getEveryPage());
            
        } else {
            
            Supplier supplier = genericDaoSupport.load(Supplier.class, login.getSupplierId());
            return vehicleSourceDao.list(status, supplier, page.getBeginIndex(), page.getEveryPage());
            
        }
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#count(com.demo2do.fms.admin.entity.search.supplier.VehicleSourceSearch)
     */
    public int count(VehicleSourceSearch search) {
        String sentence = search.buildSentenceTemplate();
        Map<String, Object> parameters = search.buildParameters();
        return genericDaoSupport.count(sentence, parameters);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#search(com.demo2do.fms.admin.entity.search.supplier.VehicleSourceSearch, com.demo2do.core.entity.page.Page)
     */
    @SuppressWarnings("unchecked")
    public List<VehicleSource> search(VehicleSourceSearch search, Page page) {
        
        // 1. build sentence
        String sentence = search.buildSentenceTemplate();
        Map<String, Object> parameters = search.buildParameters();
        
        // 2. do query
        List<VehicleSource> vehicleSources = genericDaoSupport.searchForList(sentence, parameters, page.getBeginIndex(), page.getEveryPage());
        page.calculate();
        return vehicleSources;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#bind(com.demo2do.fms.entity.vehicle.Tractor, java.lang.Long, com.demo2do.fms.admin.entity.security.Login)
     */
    public void bind(Tractor tractor, Long trailerId, Login login) {
        
        Date now = new Date();
        Long operator = login.getId();

        // 1. validate
        this.validteBind(tractor, trailerId, login);
        
        // 2. initialize vehicle_source
        Supplier supplier = supplierDao.loadSupplier(login.getSupplierId());
        Organization organization = genericDaoSupport.load(Organization.class, supplier.getOrgId());
        
        VehicleSource source = null;
        if (trailerId == null) {                                // 未绑定挂车
            
            source = new VehicleSource(tractor, supplier, organization, now, operator);
            
            // 3. update tractor bind
            genericDaoSupport.update(tractor.bind().operate(now, operator));
            
        } else {                                                // 已绑定挂车
            
            Trailer trailer = genericDaoSupport.load(Trailer.class, trailerId);
            source = new VehicleSource(tractor, trailer, supplier, organization, now, operator);
            
            // 3. update tractor & trailer bind
            genericDaoSupport.update(tractor.bind().operate(now, operator));
            genericDaoSupport.update(trailer.bind().operate(now, operator));
            
        }
        
        // 4. sync old vehicle info
        source = this.syncOldVehicleSource(source);
        genericDaoSupport.save(source);
        
        // 5. save vehicle_source event
        genericDaoSupport.save(new VehicleSourceEvent(source, VehicleSourceEventType.BIND, operator));
        
        // 5. sync vehicle to synced_status
        this.sync(source, operator, now);
        
    }
    
    /**
     * 校验牵引车、挂车合法性
     * 
     * @param tractor
     * @param trailerId
     * @param login
     */
    private void validteBind(Tractor tractor, Long trailerId, Login login) {
        
        // 1.1 validate request data
        if (!login.isSupplier()) {
            throw BusinessException.newInstance("500", "非供方，无法绑定车辆");
        }
        
        // 1.2 validate tractor_trainload
        if (tractor.requireTrailer() && trailerId == null) {
            throw BusinessException.newInstance("500", "此牵引车必须绑定挂车");
        }
        
        // 2. validate unique by tractor type
        if (tractor.requireTrailer()) {
            
            String validMessage = this.checkUniqueTractor(tractor);
            if (StringUtils.isNotBlank(validMessage)) {
                throw BusinessException.newInstance("500", validMessage);
            }
            
            Trailer trailer = genericDaoSupport.load(Trailer.class, trailerId);
            validMessage = this.checkUniqueTrailer(trailer);
            if (StringUtils.isNotBlank(validMessage)) {
                throw BusinessException.newInstance("500", validMessage);
            }
            
        } else {
            
            String validMessage = this.checkUniqueTractor(tractor);
            if (StringUtils.isNotBlank(validMessage)) {
                throw BusinessException.newInstance("500", validMessage);
            }
            
        }
        
    }
    
    /**
     * 校验牵引车唯一性
     * 
     * @param tractor
     * @return
     */
    private String checkUniqueTractor(Tractor tractor) {
        
        if (StringUtils.isNotBlank(tractor.getBrandCNCode())) {
            VehicleSource source = vehicleSourceDao.getByTractorCnCode(tractor.getBrandCNCode());
            if (source != null) {
                Supplier supplier = source.getSupplier();
                return "该牵引车中文车牌号已存在！车辆所属供方=" + supplier.getCode();
            }
        }
        
        return null;
        
    }
    
    /**
     * 校验挂车唯一性
     * 
     * @param trailer
     * @return
     */
    private String checkUniqueTrailer(Trailer trailer) {
        
        if (StringUtils.isNotBlank(trailer.getBrandCNCode())) {
            VehicleSource source = vehicleSourceDao.getByTrailerCnCode(trailer.getBrandCNCode());
            if (source != null) {
                Supplier supplier = source.getSupplier();
                return "该挂车中文车牌号已存在！车辆所属供方=" + supplier.getCode() + "！ 车辆牵引车中文车牌号=" + source.getTractorBrandCode();
            }
        }
        
        return null;
        
    }
    
    /**
     * @param vehicleSource
     * @return
     */
    private VehicleSource syncOldVehicleSource(VehicleSource vehicleSource) {
        
        String brand = vehicleSource.getTractorBrandCode();
        
        // 查询是否存在已disable，车牌号为新建车辆车牌号，code不为空的车辆
        VehicleSource oldVehicle = vehicleSourceDao.loadByBrand(brand);
        if(oldVehicle != null) {
            
            // 更新新建车辆锁定信息
            boolean couldUnlock = StringUtils.isBlank(this.validUnlock(vehicleSource));
            if (BooleanUtils.isTrue(oldVehicle.getLock()) && !couldUnlock) {
                vehicleSource.syncLockInfo(oldVehicle.getLock(), oldVehicle.getLockType(), oldVehicle.getLockReason());
            }
            
        }
        
        return vehicleSource;
        
    }
    
    /**
     * @param vehicleSource
     * @return
     */
    private String validUnlock(VehicleSource vehicleSource) {
        
        if(!vehicleSource.isSynchronize()) {
            return "车辆暂未同步";
        }
        
        if(StringUtils.isEmpty(vehicleSource.getCode())) {
            return "车辆缺失code，数据异常";
        }
        
        Vehicle vehicle = vehicleDao.loadByCode(vehicleSource.getCode());
        List<Driver> drivers = driverDao.listByVehicle(vehicle.getId(), DriverAuditStatus.QUALIFIED);
        if (org.apache.commons.collections.CollectionUtils.isEmpty(drivers)) {
            return "车辆未和司机绑定";
        }
        
        Date twoHoursBefore = DateUtils.addHours(new Date(), -2);
        if(!this.hasLastTwoHourGpsInfo(vehicleSource, twoHoursBefore)) {
            return "无最近两小时的GPS信息";
        }
        
        return null;
        
    }
    
    /**
     * @param vehicleSource
     * @param twoHoursBefore
     * @return
     */
    private boolean hasLastTwoHourGpsInfo(VehicleSource vehicleSource, Date twoHoursBefore) {
        
        // 1. get vw_vehicle info
        String vehicleCode = vehicleSource.getCode();
        Vehicle vehicle = vehicleDao.loadByCode(vehicleCode);
        
        // 2. get vw_vehicle whether has gps info in 2 hour
        List<VehicleGpsInfo> infos = remoteTransMeansDao.listLatestGps(vehicle.getId(), twoHoursBefore);
        return org.apache.commons.collections.CollectionUtils.isNotEmpty(infos);
        
    }
    
    /**
     * 对原已同步的车辆（非冻结），绑定后直接变已同步状态
     * 
     * @param vehicleSource
     * @param operator
     * @param now
     */
    private void sync(VehicleSource vehicleSource, Long operator, Date now) {
        
        if (vehicleSource.hasCode()) {
            
            LockType lockType = vehicleSource.getLockType();
            if (!lockType.isFreeze()) {
                
                // 1 edit vehicle source
                genericDaoSupport.update(vehicleSource.sign(operator, now));
                
                Tractor tractor = genericDaoSupport.load(Tractor.class, vehicleSource.getTractorId());
                genericDaoSupport.update(tractor.sign());
                
                if (vehicleSource.hasTrailer()) {
                    Trailer trailer = genericDaoSupport.load(Trailer.class, vehicleSource.getTrailerId());
                    genericDaoSupport.update(trailer.sign());
                }
                
                // 2 edit remote transMeans
                String vehicleCode = vehicleSource.getCode();
                RemoteTransMeans remoteTransMeans = remoteTransMeansDao.loadByCode(vehicleCode);
                if(remoteTransMeans != null) {
                    genericDaoSupport.update(remoteTransMeans.editGps(tractor.getSimCard(), now));
                }
                
                // 3 unlock vehicle_source
                if (vehicleSource.couldUnlock()) {
                    this.unlockVehicle(vehicleSource, operator, "挂车绑定解锁");
                }
                
                // 4. save vehicle_source event
                genericDaoSupport.save(new VehicleSourceEvent(vehicleSource, VehicleSourceEventType.SYNC, operator));
                
            }
            
        }
        
    }
    
    /*
     * (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#delete(com.demo2do.fms.entity.vehicle.VehicleSource)
     */
    public void unbind(VehicleSource vehicleSource, Long operator) {
        
        Date now = new Date();
        
        // 1. delete vehicle_source
        genericDaoSupport.update(vehicleSource.disable(operator));
        
        // 2. unbind tractor && trailer
        Tractor tractor = genericDaoSupport.load(Tractor.class, vehicleSource.getTractorId());
        genericDaoSupport.update(tractor.unbind().operate(now, operator));
        
        if (vehicleSource.hasTrailer()) {
            Trailer trailer = genericDaoSupport.load(Trailer.class, vehicleSource.getTrailerId());
            genericDaoSupport.update(trailer.unbind().operate(now, operator));
        }
        
        // 3. lock vehicle
        if(vehicleSource.couldLock()) {
            this.lockVehicle(vehicleSource, "解绑整车，调度异常锁定", LockType.SHIPMENT_EXCEPTION, operator, "解绑车辆锁定");
        }
        
        // 4. save vehicle_source event
        genericDaoSupport.save(new VehicleSourceEvent(vehicleSource, VehicleSourceEventType.UNBIND, operator));
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#loadVehicleSource(java.lang.Long)
     */
    public VehicleSource loadVehicleSource(Long trailerId) {
        return vehicleSourceDao.loadByTrailer(trailerId);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#changeTrailer(com.demo2do.fms.entity.vehicle.VehicleSource, java.lang.Long, java.lang.Long)
     */
    public void changeTrailer(VehicleSource vehicleSource, Long trailerId, Long operator) {
        
        Date now = new Date();
        
        // 1. 被更换整车解绑
        Trailer changeTrailer = genericDaoSupport.load(Trailer.class, trailerId);
        if (changeTrailer.isBind()) {
            
            VehicleSource changeVehicleSource = vehicleSourceDao.loadByTrailer(trailerId);
            genericDaoSupport.update(changeVehicleSource.disable(operator));
            if(changeVehicleSource.couldLock()) {
                this.lockVehicle(changeVehicleSource, "换挂，调度异常锁定", LockType.SHIPMENT_EXCEPTION, operator, "更换挂车锁定");
            }
            
            Tractor changeTractor = genericDaoSupport.load(Tractor.class, changeVehicleSource.getTractorId());
            genericDaoSupport.update(changeTractor.unbind().operate(now, operator));
            
        }
        
        // 2. 本整车换挂
        if (vehicleSource.hasTrailer()) {
            Trailer originalTrailer = genericDaoSupport.load(Trailer.class, vehicleSource.getTrailerId());
            genericDaoSupport.update(originalTrailer.unbind().operate(now, operator));
        }
        
        genericDaoSupport.update(vehicleSource.editTrailer(changeTrailer).operate(now, operator));
        genericDaoSupport.update(changeTrailer.bind().sign(vehicleSource.getSynchronize()).operate(now, operator));
        
        // 3. save vehicle_source event
        genericDaoSupport.save(new VehicleSourceEvent(vehicleSource, VehicleSourceEventType.CHANGE_TRAILER, operator));
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#audit(com.demo2do.fms.entity.vehicle.VehicleSource, java.lang.Long)
     */
    public void declare(VehicleSource vehicleSource, Long operator) {
        
        Date now = new Date();
        
        // 1. check vehicle could declare
        validDeclare(vehicleSource);
        
        if (vehicleSource.hasCode()) {      // 2. 如果是原先已同步过的车，直接到已同步状态(除了转出的车)
            
            LockType lockType = vehicleSource.getLockType();
            if (lockType.isFreeze()) {       // 2.1 已同步过的车，冻结状态，还需要到待同步状态
                
                // 2.1 declare vehicle_source
                genericDaoSupport.update(vehicleSource.declare(operator));
                
                // 2.2 save vehicle_source event
                genericDaoSupport.save(new VehicleSourceEvent(vehicleSource, VehicleSourceEventType.DECLARE, operator));
                
            } else {                         // 2.2 已同步过的车，非冻结状态，直接到已同步状态
                
                // 2.1 sign vehicle
                genericDaoSupport.update(vehicleSource.sign(operator, now));
                
                Tractor tractor = genericDaoSupport.load(Tractor.class, vehicleSource.getTractorId());
                genericDaoSupport.update(tractor.sign());
                
                if (vehicleSource.hasTrailer()) {
                    Trailer trailer = genericDaoSupport.load(Trailer.class, vehicleSource.getTrailerId());
                    genericDaoSupport.update(trailer.sign());
                }
                
                // 2.2 edit remote transMeans
                String vehicleCode = vehicleSource.getCode();
                RemoteTransMeans remoteTransMeans = remoteTransMeansDao.loadByCode(vehicleCode);
                if(remoteTransMeans != null) {
                    genericDaoSupport.update(remoteTransMeans.editGps(tractor.getSimCard(), now));
                }
                
                // 2.3 unlock vehicle_source
                if (vehicleSource.couldUnlock()) {
                    this.unlockVehicle(vehicleSource, operator, "挂车绑定解锁");
                }
                
                // 2.4 save vehicle_source event
                genericDaoSupport.save(new VehicleSourceEvent(vehicleSource, VehicleSourceEventType.SYNC, operator));
                
            }
            
        } else {                            // 3. 之前未同步过的车，先到待同步状态
            
            // 3.1 declare vehicle
            genericDaoSupport.update(vehicleSource.declare(operator));
            
            // 3.2 save vehicle_source event
            genericDaoSupport.save(new VehicleSourceEvent(vehicleSource, VehicleSourceEventType.DECLARE, operator));
            
        }
        
    }
    
    /**
     * @param vehicleSource
     */
    private void validDeclare(VehicleSource vehicleSource) {
        
        if (vehicleSource.hasTrailer()) {
            
            Tractor tractor = genericDaoSupport.load(Tractor.class, vehicleSource.getTractorId());
            Trailer trailer = genericDaoSupport.load(Trailer.class, vehicleSource.getTrailerId());
            if (!tractor.getStatus().isQualified() || !trailer.getStatus().isQualified()) {
                throw BusinessException.newInstance("500", "牵引车或挂车不合格，不能引入申报");
            }
            
        } else {
            
            Tractor tractor = genericDaoSupport.load(Tractor.class, vehicleSource.getTractorId());
            if (!tractor.getStatus().isQualified()) {
                throw BusinessException.newInstance("500", "牵引车不合格，不能引入申报");
            }
            
        }
        
    }
    
    /**
     * @param vehicleSource
     * @param operator
     * @param description
     */
    private void unlockVehicle(VehicleSource vehicleSource, Long operator, String description) {
        
        Date now = new Date();
        
        // 1. unlock fms vehicle
        genericDaoSupport.update(vehicleSource.unlock(operator, now));
        
        String vehicleCode = vehicleSource.getCode();
        RemoteTransMeans remoteTransMeans = remoteTransMeansDao.loadByCode(vehicleCode);
        
        if(remoteTransMeans == null) {
            throw new BusinessException("该车辆在MDM中不存在");
        }
        if(!remoteTransMeans.isAvaliable()) {
            throw new BusinessException("MDM中该车辆为非正常车辆");
        }
        
        // 2. lock mdm vehicle
        genericDaoSupport.update(remoteTransMeans.unlock(Constants.MDM_LOCK_USERID));
        
        // 3. create lock record
        VehicleSourceLockRecord record = new VehicleSourceLockRecord(vehicleSource, LockStatus.UNLOCKED, description, operator);
        genericDaoSupport.save(record);
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#cancelDeclare(com.demo2do.fms.entity.vehicle.VehicleSource, java.lang.Long)
     */
    public void cancelDeclare(VehicleSource vehicleSource, Long operator) {
        
        // 1. update vehicle_source
        genericDaoSupport.update(vehicleSource.cancelDeclare(operator));
        
        // 2. save vehicle_source event
        genericDaoSupport.save(new VehicleSourceEvent(vehicleSource, VehicleSourceEventType.CANCEL, operator));
        
    }

    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#countDeclare()
     */
    public int countDeclare() {
        return vehicleSourceDao.count(SyncStatus.SYNCING);
    }
    
    /**
     * @param page
     * @return
     */
    public List<VehicleSource> listDeclare(Page page) {
        page.calculate();
        return vehicleSourceDao.list(SyncStatus.SYNCING, page.getBeginIndex(), page.getEveryPage());
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#sign(java.lang.Long, java.lang.Long)
     */
    public void sign(Long vehicleId, Long operator) {
        
        Date now = new Date();
        
        // 1. validate vehicle code
        VehicleSource vehicleSource = genericDaoSupport.load(VehicleSource.class, vehicleId);
        if (!vehicleSource.hasCode()) {
            throw BusinessException.newInstance("500", "FMS车辆code不存在");
        }
        
        // 2. sign vehicle
        Tractor tractor = genericDaoSupport.load(Tractor.class, vehicleSource.getTractorId());
        genericDaoSupport.update(tractor.sign(vehicleSource.getCode()));
        
        if (vehicleSource.hasTrailer()) {
            Trailer trailer = genericDaoSupport.load(Trailer.class, vehicleSource.getTrailerId());
            genericDaoSupport.update(trailer.sign());
        }
        
        genericDaoSupport.update(vehicleSource.sign(operator, now));
        
        // 3. edit remote transMeans
        String vehicleCode = vehicleSource.getCode();
        RemoteTransMeans remoteTransMeans = remoteTransMeansDao.loadByCode(vehicleCode);
        genericDaoSupport.update(remoteTransMeans.editGps(tractor.getSimCard(), now));
        
        // 4. save vechiel_source event
        genericDaoSupport.save(new VehicleSourceEvent(vehicleSource, VehicleSourceEventType.SYNC, operator));
        
        // 5. send vehicle info to vam
        if (vehicleSource.isVam()) {
            vamHandler.syncVehicleCode(tractor, remoteTransMeans.getName());
        }
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#batchSign(java.util.List, java.lang.Long)
     */
    public int batchSign(List<Long> vehicleIds, Long operator) {
        
        int failNumber = 0;
        
        for (Long vehicleId : vehicleIds) {
        
            try {
                
                this.sign(vehicleId, operator);
                
            } catch (Exception e) {
                failNumber++;
                logger.error("#batchSign() - batch sign failed, vehicleId=[{}], exception=[{}]", vehicleId, e);
            }
            
        }
        
        return failNumber;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#reject(com.demo2do.fms.entity.vehicle.VehicleSource, java.lang.Long)
     */
    public void reject(VehicleSource vehicleSource, Long operator) {
        
        // 1. reject vehicle_source
        genericDaoSupport.update(vehicleSource.reject(operator));
        
        // 2. save vehicle_source event
        genericDaoSupport.save(new VehicleSourceEvent(vehicleSource, VehicleSourceEventType.REJECT, operator));
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#out(com.demo2do.fms.entity.vehicle.VehicleSource, java.lang.Long)
     */
    public void out(VehicleSource vehicleSource, Long operator) {
        
        Date now = new Date();
        
        // 1. lock table
        this.lockVehicle(vehicleSource, "转出整车冻结", LockType.FREEZE, operator, "转出冻结");
        
        // 2. out vehicle
        Tractor tractor = genericDaoSupport.load(Tractor.class, vehicleSource.getTractorId());
        genericDaoSupport.update(tractor.out(operator));
        genericDaoSupport.save(new TractorEvent(tractor, TractorEventType.OUT, operator));
        
        if (vehicleSource.hasTrailer()) {
            Trailer trailer = genericDaoSupport.load(Trailer.class, vehicleSource.getTrailerId());
            genericDaoSupport.update(trailer.out(operator));
            genericDaoSupport.save(new TrailerEvent(trailer, TrailerEventType.OUT, operator));
        }
        
        genericDaoSupport.update(vehicleSource.out(operator));
        genericDaoSupport.save(new VehicleSourceEvent(vehicleSource, VehicleSourceEventType.OUT, operator));
        
        // 3. unbind driver and vehicle
        if(vehicleSource.isSynchronize()) {
            Vehicle vehicle = vehicleDao.loadByCode(vehicleSource.getCode());
            if(vehicle != null) {
                List<Driver> drivers = driverDao.listByVehicle(vehicle.getId(), DriverAuditStatus.QUALIFIED);
                for(Driver driver : drivers) {
                    genericDaoSupport.update(driver.unbind(operator, now));
                }
            }
        }
        
    }
    
    /**
     * @param vehicleSource
     * @param reason
     * @param type
     * @param operator
     * @param description
     * @return
     */
    private VehicleSource lockVehicle(VehicleSource vehicleSource, String reason, LockType type, Long operator, String description) {
        
        Date now = new Date();
        
        // 1. 锁定FMS车辆
        genericDaoSupport.update(vehicleSource.doLock(reason, type, operator, now));
        
        String vehicleCode = vehicleSource.getCode();
        RemoteTransMeans remoteTransMeans = remoteTransMeansDao.loadByCode(vehicleCode);
        
        if(remoteTransMeans == null) {
            throw new BusinessException("MDM锁车数据信息为空");
        }
        if(!remoteTransMeans.isAvaliable()) {
            throw new BusinessException("MDM中该车辆为非正常车辆");
        }
        
        // 2. 锁定MDM车辆
        genericDaoSupport.update(remoteTransMeans.lock(type, Constants.MDM_LOCK_USERID));
        
        // 3. 创建锁车记录
        VehicleSourceLockRecord record = new VehicleSourceLockRecord(vehicleSource, LockStatus.LOCKED, description, operator);
        genericDaoSupport.save(record);
        
        return vehicleSource;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#countQualified(com.demo2do.fms.admin.entity.security.Login)
     */
    public int countQualified(Login login) {
        
        if (login.isSupplier()) {
            
            Supplier supplier = genericDaoSupport.load(Supplier.class, login.getSupplierId());
            return vehicleSourceDao.count(supplier);
            
        } else {
            return vehicleSourceDao.count();
        }
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#listQualified(com.demo2do.fms.admin.entity.security.Login, com.demo2do.core.entity.page.Page)
     */
    public List<VehicleSource> listQualified(Login login, Page page) {
        
        page.calculate();
        
        if (login.isSupplier()) {
            
            Supplier supplier = genericDaoSupport.load(Supplier.class, login.getSupplierId());
            return vehicleSourceDao.list(supplier, page.getBeginIndex(), page.getEveryPage());
            
        } else {
            return vehicleSourceDao.list(page.getBeginIndex(), page.getEveryPage());
        }
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#countSubVehicle(com.demo2do.fms.admin.entity.security.Login)
     */
    public int countSub(Login login) {
        
        if (!login.isSupplier()) {  // 总部或分区用户, 拿所有整车
            
            return vehicleSourceDao.count();
            
        } else {                    // 一级供应商， 拿所属二级供应商整车
            
            Supplier supplier = genericDaoSupport.load(Supplier.class, login.getSupplierId());
            List<Long> supplierIds = supplierRelationDao.listSponsoredSupplierId(supplier);
            
            if (org.apache.commons.collections.CollectionUtils.isEmpty(supplierIds)) {
                return 0;
            }
            return vehicleSourceDao.countBySupplierIds(supplierIds);
            
        }
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#listSubVehicle(com.demo2do.fms.admin.entity.security.Login, com.demo2do.core.entity.page.Page)
     */
    public List<VehicleSource> listSub(Login login, Page page) {

        page.calculate();
        
        if (!login.isSupplier()) {  // 总部或分区用户, 拿所有整车
            
            return vehicleSourceDao.list(page.getBeginIndex(), page.getEveryPage());
            
        } else {                    // 一级供应商， 拿所属二级供应商整车
            
            Supplier supplier = genericDaoSupport.load(Supplier.class, login.getSupplierId());
            List<Long> supplierIds = supplierRelationDao.listSponsoredSupplierId(supplier);
            return vehicleSourceDao.listBySupplierIds(supplierIds, page.getBeginIndex(), page.getEveryPage());
            
        }
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#countSubVehicle(com.demo2do.fms.admin.entity.search.supplier.VehicleSourceSearch, com.demo2do.fms.admin.entity.security.Login)
     */
    public int countSub(VehicleSourceSearch search, Login login) {
        
        if (login.isSupplier()) {
            Supplier loginSupplier = genericDaoSupport.load(Supplier.class, login.getSupplierId());
            List<Long> supplierIds = supplierRelationDao.listSponsoredSupplierId(loginSupplier);
            search.addSupplierId(supplierIds);
        }
        
        // 1. build sentence
        String sentence = search.buildSentenceTemplate();
        Map<String, Object> parameters = search.buildParameters();
        
        // 2. do query
        return genericDaoSupport.count(sentence, parameters);
        
    }
    
    /*
     * (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#searchSubVehicle(com.demo2do.fms.admin.entity.search.supplier.VehicleSourceSearch, java.lang.String, java.lang.String, com.demo2do.fms.admin.entity.security.Login, com.demo2do.core.entity.page.Page)
     */
    @SuppressWarnings("unchecked")
    public List<VehicleSource> searchSub(VehicleSourceSearch search, Login login, Page page) {
        
        if (login.isSupplier()) {
            Supplier loginSupplier = genericDaoSupport.load(Supplier.class, login.getSupplierId());
            List<Long> supplierIds = supplierRelationDao.listSponsoredSupplierId(loginSupplier);
            search.addSupplierId(supplierIds);
        }
        
        // 1. build sentence
        String sentence = search.buildSentenceTemplate();
        Map<String, Object> parameters = search.buildParameters();
        
        // 2. do query
        List<VehicleSource> vehicleSources = genericDaoSupport.searchForList(sentence, parameters, page.getBeginIndex(), page.getEveryPage());
        page.calculate();
        return vehicleSources;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#loadVehicleGpsInfo(java.lang.Long)
     */
    public VehicleGpsInfo loadVehicleGpsInfo(Long id) {
    	return vehicleDao.loadVehicleGpsInfo(id);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#generateExport(java.util.List)
     */
    @SuppressWarnings("unchecked")
    public List<VehicleSourceSheet> generateExport(List<Long> vehicleIds) {
        
        List<VehicleSource> vehicleSources = vehicleSourceDao.list(vehicleIds);
        
        // 根据vehicleSource中的tractorId查询牵引车
        List<Long> tractorIds = CollectionUtils.extractPropertyList(vehicleSources, "tractorId");
        List<Tractor> tractors = tractorDao.list(tractorIds);
        Map<String, Tractor> tractorMap = CollectionUtils.constructMapValue(tractors, "id");
        
        // 根据vehicleSource中的organizationId查询公司
        List<Long> orgIds = CollectionUtils.extractPropertyList(vehicleSources, "organization.id");
        List<Organization> organizations = organizationDao.list(orgIds);
        Map<String, Organization> orgMap = CollectionUtils.constructMapValue(organizations, "id");
        
        List<VehicleSourceSheet> vehicleSourceSheets = new ArrayList<VehicleSourceSheet>();
        for(VehicleSource vehicleSource : vehicleSources) {
        	
        	Tractor tractor = tractorMap.get(vehicleSource.getTractorId().toString());
        	Supplier supplier = vehicleSource.getSupplier();
        	
        	// 车辆工具条形码
        	String barCode = null; 				// 车辆工具条形码
            if (supplier != null) {
            	barCode = this.getBarCode(supplier, tractor.getBrandENCode());
            }
            
            VehicleSourceSheet vehicleSourceSheet = new VehicleSourceSheet(vehicleSource, tractor, 
                                                                           orgMap.get(vehicleSource.getOrganization().getId().toString()),
                                                                           barCode);
            vehicleSourceSheets.add(vehicleSourceSheet);
            
        }
        
        return vehicleSourceSheets;
        
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.fms.admin.service.supplier.VehicleSourceService#batchExport(java.util.List)
     */
    public List<Map<String, Object>> export(VehicleSourceReportSearch search) {
        String sentence = search.buildSentenceTemplate();
        Map<String, Object> parameters = search.buildParameters();
        RowMapper<Map<String, Object>> rowMapper = search.buildRowMapper();
        return fmsJdbcSupport.queryForList(sentence, parameters, rowMapper);
    }
    
    /**
     * @param supplier
     * @param tractorENBrandCode
     * @return
     */
    private String getBarCode(Supplier supplier, String tractorENBrandCode) {
    	
    	String supplierCode = supplier.getCode();
    	String barCode = null;
    	
    	if (StringUtils.length(supplierCode) > 2) {		// code长度大于2
    		barCode = com.demo2do.core.utils.StringUtils.connect(supplierCode, tractorENBrandCode);
    	} else {										// code长度小于等于2
    		barCode = com.demo2do.core.utils.StringUtils.connect(supplierCode, "80", tractorENBrandCode);
    	}
    	
    	return barCode;
    	
    }

}